home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Files / Standard File / StandardGetFolder / Search.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  35.9 KB  |  1,278 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        Search.c
  3.     
  4.     Description:IndexedSearch and the PBCatSearch compatibility function.
  5.  
  6.     Author:        JL
  7.  
  8.     Copyright:     Copyright: © 1992-1999 by Apple Computer, Inc.
  9.                 all rights reserved.
  10.     
  11.     Disclaimer:    You may incorporate this sample code into your applications without
  12.                 restriction, though the sample code has been provided "AS IS" and the
  13.                 responsibility for its operation is 100% yours.  However, what you are
  14.                 not permitted to do is to redistribute the source as "DSC Sample Code"
  15.                 after having made changes. If you're going to re-distribute the source,
  16.                 we require that you make it clear in the source that the code was
  17.                 descended from Apple Sample Code, but that you've made changes.
  18.     
  19.     Change History (most recent first):
  20.                 6/25/99    Updated for Metrowerks Codewarror Pro 2.1(KG)
  21.  
  22. */
  23.  
  24. #include <Types.h>
  25. #include <Gestalt.h>
  26. #include <Timer.h>
  27. #include <Errors.h>
  28. #include <Memory.h>
  29. #include <Files.h>
  30. #include <TextUtils.h>
  31.  
  32. #define    __COMPILINGMOREFILES
  33.  
  34. #include "MoreFiles.h"
  35. #include "MoreFilesExtras.h"
  36. #include "Search.h"
  37.  
  38. /*****************************************************************************/
  39.  
  40. enum
  41. {
  42.     /* Number of LevelRecs to add each time the searchStack is grown */
  43.     /* 20 levels is probably more than reasonable for most volumes. */
  44.     /* If more are needed, they are allocated 20 levels at a time. */
  45.     kAdditionalLevelRecs = 20
  46. };
  47.  
  48. /*****************************************************************************/
  49.  
  50. /*
  51. **    LevelRecs are used to store the directory ID and index whenever
  52. **    IndexedSearch needs to either scan a sub-directory, or return control
  53. **    to the caller because the call has timed out or the number of
  54. **    matches requested has been found. LevelRecs are stored in an array
  55. **    used as a stack.
  56. */
  57. struct    LevelRec
  58. {
  59.     long    dirModDate;    /* for detecting most (but not all) catalog changes */
  60.     long    dirID;
  61.     short    index;
  62. };
  63. typedef struct LevelRec LevelRec;
  64. typedef LevelRec *LevelRecPtr, **LevelRecHandle;
  65.  
  66.  
  67. /*
  68. **    SearchPositionRec is my version of a CatPositionRec. It holds the
  69. **    information I need to resuming searching.
  70. */
  71. #if PRAGMA_ALIGN_SUPPORTED
  72. #pragma options align=mac68k
  73. #endif
  74. struct SearchPositionRec
  75. {
  76.     long            initialize;        /* Goofy checksum of volume information used to make */
  77.                                     /* sure we're resuming a search on the same volume. */
  78.     unsigned short    stackDepth;        /* Current depth on searchStack. */
  79.     short            priv[11];        /* For future use... */
  80. };
  81. #if PRAGMA_ALIGN_SUPPORTED
  82. #pragma options align=reset
  83. #endif
  84. typedef struct SearchPositionRec SearchPositionRec;
  85. typedef SearchPositionRec *SearchPositionRecPtr;
  86.  
  87.  
  88. /*
  89. **    ExtendedTMTask is a TMTask record extended to hold the timer flag.
  90. */
  91. #if PRAGMA_ALIGN_SUPPORTED
  92. #pragma options align=mac68k
  93. #endif
  94. struct ExtendedTMTask
  95. {
  96.     TMTask            theTask;
  97.     Boolean            stopSearch;        /* the Time Mgr task will set stopSearch to */
  98.                                     /* true when the timer expires */
  99. };
  100. #if PRAGMA_ALIGN_SUPPORTED
  101. #pragma options align=reset
  102. #endif
  103. typedef struct ExtendedTMTask ExtendedTMTask;
  104. typedef ExtendedTMTask *ExtendedTMTaskPtr;
  105.  
  106. /*****************************************************************************/
  107.  
  108. static    OSErr    CheckVol(ConstStr255Param pathname,
  109.                          short vRefNum,
  110.                          short *realVRefNum,
  111.                          long *volID);
  112.  
  113. static    OSErr    CheckStack(unsigned short stackDepth,
  114.                            LevelRecHandle searchStack,
  115.                            Size *searchStackSize);
  116.  
  117. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  118.                              Boolean *includeFiles,
  119.                              Boolean *includeDirs,
  120.                              Boolean *includeNames);
  121.  
  122. static    Boolean    IsSubString(ConstStr255Param aStringPtr,
  123.                             ConstStr255Param subStringPtr);
  124.  
  125. static    Boolean    CompareMasked(const long *data1,
  126.                               const long *data2,
  127.                               const long *mask,
  128.                               short longsToCompare);
  129.  
  130. static    void    CheckForMatches(CInfoPBPtr cPB,
  131.                                 CSParamPtr userPB,
  132.                                 const Str63 matchName,
  133.                                 Boolean includeFiles,
  134.                                 Boolean includeDirs);
  135.  
  136. #if    __WANTPASCALELIMINATION
  137. #undef    pascal
  138. #endif
  139.  
  140. #if GENERATINGCFM
  141.  
  142. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr);
  143.  
  144. #else
  145.  
  146. static    pascal    TMTaskPtr    GetTMTaskPtr(void);
  147.  
  148. static    void    TimeOutTask(void);
  149.  
  150. #endif
  151.  
  152. #if    __WANTPASCALELIMINATION
  153. #define    pascal    
  154. #endif
  155.  
  156. static    long    GetDirModDate(short vRefNum,
  157.                               long dirID);
  158.  
  159. /*****************************************************************************/
  160.  
  161. /*
  162. **    CheckVol gets the volume's real vRefNum and builds a volID. The volID
  163. **    is used to help insure that calls to resume searching with IndexedSearch
  164. **    are to the same volume as the last call to IndexedSearch.
  165. */
  166. static    OSErr    CheckVol(ConstStr255Param pathname,
  167.                          short vRefNum,
  168.                          short *realVRefNum,
  169.                          long *volID)
  170. {
  171.     HParamBlockRec pb;
  172.     OSErr error;
  173.  
  174.     error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
  175.     if ( error == noErr )
  176.     {
  177.         /* Return the real vRefNum */
  178.         *realVRefNum = pb.volumeParam.ioVRefNum;
  179.  
  180.         /* Add together a bunch of things that aren't supposed to change on */
  181.         /* a mounted volume that's being searched and that should come up with */
  182.         /* a fairly unique number */
  183.         *volID = pb.volumeParam.ioVCrDate +
  184.                  pb.volumeParam.ioVRefNum +
  185.                  pb.volumeParam.ioVNmAlBlks +
  186.                  pb.volumeParam.ioVAlBlkSiz +
  187.                  pb.volumeParam.ioVFSID;
  188.     }
  189.     return ( error );
  190. }
  191.  
  192. /*****************************************************************************/
  193.  
  194. /*
  195. **    CheckStack checks the size of the search stack (array) to see if there's
  196. **    room to push another LevelRec. If not, CheckStack grows the stack by
  197. **    another kAdditionalLevelRecs elements.
  198. */
  199. static    OSErr    CheckStack(unsigned short stackDepth,
  200.                            LevelRecHandle searchStack,
  201.                            Size *searchStackSize)
  202. {
  203.     OSErr    result;
  204.     
  205.     if ( (*searchStackSize / sizeof(LevelRec)) == (stackDepth + 1) )
  206.     {
  207.         /* Time to grow stack */
  208.         SetHandleSize((Handle)searchStack, *searchStackSize + (kAdditionalLevelRecs * sizeof(LevelRec)));
  209.         result = MemError();    /* should be noErr */
  210.         *searchStackSize = InlineGetHandleSize((Handle)searchStack);
  211.     }
  212.     else
  213.     {
  214.         result = noErr;
  215.     }
  216.     
  217.     return ( result );
  218. }
  219.  
  220. /*****************************************************************************/
  221.  
  222. /*
  223. **    VerifyUserPB makes sure the parameter block passed to IndexedSearch has
  224. **    valid parameters. By making this check once, we don't have to worry about
  225. **    things like NULL pointers, strings being too long, etc.
  226. **    VerifyUserPB also determines if the search includes files and/or
  227. **    directories, and determines if a full or partial name search was requested.
  228. */
  229. static    OSErr    VerifyUserPB(CSParamPtr userPB,
  230.                              Boolean *includeFiles,
  231.                              Boolean *includeDirs,
  232.                              Boolean *includeNames)
  233. {
  234.     CInfoPBPtr    searchInfo1;
  235.     CInfoPBPtr    searchInfo2;
  236.     
  237.     searchInfo1 = userPB->ioSearchInfo1;
  238.     searchInfo2 = userPB->ioSearchInfo2;
  239.     
  240.     /* ioMatchPtr cannot be NULL */
  241.     if ( userPB->ioMatchPtr == NULL )
  242.     {
  243.         goto ParamErrExit;
  244.     }
  245.     
  246.     /* ioSearchInfo1 cannot be NULL */
  247.     if ( searchInfo1 == NULL )
  248.     {
  249.         goto ParamErrExit;
  250.     }
  251.     
  252.     /* If any bits except partialName, fullName, or negate are set, then */
  253.     /* ioSearchInfo2 cannot be NULL because information in ioSearchInfo2 is required  */
  254.     if ( ((userPB->ioSearchBits & ~(fsSBPartialName | fsSBFullName | fsSBNegate)) != 0) &&
  255.          ( searchInfo2 == NULL ))
  256.     {
  257.         goto ParamErrExit;
  258.     }
  259.     
  260.     *includeFiles = false;
  261.     *includeDirs = false;
  262.     *includeNames = false;
  263.     
  264.     if ( (userPB->ioSearchBits & (fsSBPartialName | fsSBFullName)) != 0 )
  265.     {
  266.         /* If any kind of name matching is requested, then ioNamePtr in */
  267.         /* ioSearchInfo1 cannot be NULL or a zero-length string */
  268.         if ( (searchInfo1->hFileInfo.ioNamePtr == NULL) ||
  269.              (searchInfo1->hFileInfo.ioNamePtr[0] == 0) ||
  270.              (searchInfo1->hFileInfo.ioNamePtr[0] > (sizeof(Str63) - 1)) )
  271.         {
  272.             goto ParamErrExit;
  273.         }
  274.         
  275.         *includeNames = true;
  276.     }
  277.     
  278.     if ( (userPB->ioSearchBits & fsSBFlAttrib) != 0 )
  279.     {
  280.         /* The only attributes you can search on are the directory flag */
  281.         /* and the locked flag. */
  282.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ~(ioDirMask | 0x01)) != 0 )
  283.         {
  284.             goto ParamErrExit;
  285.         }
  286.         
  287.         /* interested in the directory bit? */
  288.         if ( (searchInfo2->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  289.         {
  290.             /* yes, so do they want just directories or just files? */
  291.             if ( (searchInfo1->hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  292.             {
  293.                 *includeDirs = true;
  294.             }
  295.             else
  296.             {
  297.                 *includeFiles = true;
  298.             }
  299.         }
  300.         else
  301.         {
  302.             /* no interest in directory bit - get both files and directories */
  303.             *includeDirs = true;
  304.             *includeFiles = true;
  305.         }
  306.     }
  307.     else
  308.     {
  309.         /* no attribute checking - get both files and directories */
  310.         *includeDirs = true;
  311.         *includeFiles = true;
  312.     }
  313.     
  314.     /* If directories are included in the search, */
  315.     /* then the locked attribute cannot be requested. */
  316.     if ( *includeDirs &&
  317.          ((userPB->ioSearchBits & fsSBFlAttrib) != 0) &&
  318.          ((searchInfo2->hFileInfo.ioFlAttrib & 0x01) != 0) )
  319.     {
  320.         goto ParamErrExit;
  321.     }
  322.     
  323.     /* If files are included in the search, then there cannot be */
  324.     /* a search on the number of files. */
  325.     if ( *includeFiles &&
  326.          ((userPB->ioSearchBits & fsSBDrNmFls) != 0) )
  327.     {
  328.         goto ParamErrExit;
  329.     }
  330.     
  331.     /* If directories are included in the search, then there cannot */
  332.     /* be a search on file lengths. */
  333.     if ( *includeDirs &&
  334.          ((userPB->ioSearchBits & (fsSBFlLgLen | fsSBFlPyLen | fsSBFlRLgLen | fsSBFlRPyLen)) != 0) )
  335.     {
  336.         goto ParamErrExit;
  337.     }
  338.     
  339.     return ( noErr );
  340.          
  341. ParamErrExit:
  342.     return ( paramErr );
  343. }
  344.  
  345. /*****************************************************************************/
  346.  
  347. /*
  348. **    IsSubString checks to see if a string is a substring of another string.
  349. **    Both input strings have already been converted to all uppercase using
  350. **    UprString (the same non-international call the File Manager uses).
  351. */
  352. static    Boolean    IsSubString(ConstStr255Param aStringPtr,
  353.                             ConstStr255Param subStringPtr)
  354. {
  355.     short    strLength;        /* length of string */
  356.     short    subStrLength;    /* length of subString */
  357.     Boolean    found;            /* result of test */
  358.     short    index;            /* current index into string */
  359.     
  360.     found = false;
  361.     strLength = aStringPtr[0];
  362.     subStrLength = subStringPtr[0];
  363.         
  364.     if ( subStrLength <= strLength)
  365.     {
  366.         register short    count;            /* search counter */
  367.         register short    strIndex;        /* running index into string */
  368.         register short    subStrIndex;    /* running index into subString */
  369.         
  370.         /* start looking at first character */
  371.         index = 1;
  372.         
  373.         /* continue looking until remaining string is shorter than substring */
  374.         count = strLength - subStrLength + 1;
  375.         
  376.         do
  377.         {
  378.             strIndex = index;    /* start string index at index */
  379.             subStrIndex = 1;    /* start subString index at 1 */
  380.             
  381.             while ( !found && (aStringPtr[strIndex] == subStringPtr[subStrIndex]) )
  382.             {
  383.                 if ( subStrIndex == subStrLength )
  384.                 {
  385.                     /* all characters in subString were found */
  386.                     found = true;
  387.                 }
  388.                 else
  389.                 {
  390.                     /* check next character of substring against next character of string */
  391.                     ++subStrIndex;
  392.                     ++strIndex;
  393.                 }
  394.             }
  395.             
  396.             if ( !found )
  397.             {
  398.                 /* start substring search again at next string character */
  399.                 ++index;
  400.                 --count;
  401.             }
  402.         } while ( count != 0 && (!found) );
  403.     }
  404.     
  405.     return ( found );
  406. }
  407.  
  408. /*****************************************************************************/
  409.  
  410. /*
  411. **    CompareMasked does a bitwise comparison with mask on 1 or more longs.
  412. **    data1 and data2 are first exclusive-ORed together resulting with bits set
  413. **    where they are different. That value is then ANDed with the mask resulting
  414. **    with bits set if the test fails. true is returned if the tests pass.
  415. */
  416. static    Boolean    CompareMasked(const long *data1,
  417.                               const long *data2,
  418.                               const long *mask,
  419.                               short longsToCompare)
  420. {
  421.     Boolean    result = true;
  422.     
  423.     while ( (longsToCompare != 0) && (result == true) )
  424.     {
  425.         /* (*data1 ^ *data2) = bits that are different, so... */
  426.         /* ((*data1 ^ *data2) & *mask) = bits that are different that we're interested in */
  427.         
  428.         if ( ((*data1 ^ *data2) & *mask) != 0 )
  429.             result = false;
  430.         
  431.         ++data1;
  432.         ++data2;
  433.         ++mask;
  434.         --longsToCompare;
  435.     }
  436.     
  437.     return ( result );
  438. }
  439.  
  440. /*****************************************************************************/
  441.  
  442. /*
  443. **    Check for matches compares the search criteria in userPB to the file
  444. **    system object in cPB. If there's a match, then the information in cPB is
  445. **    is added to the match array and the actual match count is incremented.
  446. */
  447. static    void    CheckForMatches(CInfoPBPtr cPB,
  448.                                 CSParamPtr userPB,
  449.                                 const Str63 matchName,
  450.                                 Boolean includeFiles,
  451.                                 Boolean includeDirs)
  452. {
  453.     long        searchBits;
  454.     CInfoPBPtr    searchInfo1;
  455.     CInfoPBPtr    searchInfo2;
  456.     Str63        itemName;        /* copy of object's name for partial name matching */
  457.     Boolean        foundMatch;
  458.     
  459.     foundMatch = false;            /* default to no match */
  460.     
  461.     searchBits = userPB->ioSearchBits;
  462.     searchInfo1 = userPB->ioSearchInfo1;
  463.     searchInfo2 = userPB->ioSearchInfo2;
  464.     
  465.     /* Into the if statements that go on forever... */
  466.     
  467.     if ( (cPB->hFileInfo.ioFlAttrib & ioDirMask) == 0 )
  468.     {
  469.         if (!includeFiles)
  470.         {
  471.             goto Failed;
  472.         }
  473.     }
  474.     else
  475.     {
  476.         if (!includeDirs)
  477.         {
  478.             goto Failed;
  479.         }
  480.     }
  481.     
  482.     if ( (searchBits & fsSBPartialName) != 0 )
  483.     {
  484.         if ( (cPB->hFileInfo.ioNamePtr[0] > 0) &&
  485.              (cPB->hFileInfo.ioNamePtr[0] <= (sizeof(Str63) - 1)) )
  486.         {
  487.             /* Make uppercase copy of object name */
  488.             BlockMoveData(cPB->hFileInfo.ioNamePtr,
  489.                             itemName,
  490.                             cPB->hFileInfo.ioNamePtr[0] + 1);
  491.             /* Use the same non-international call the File Manager uses */
  492.             UpperString(itemName, true);
  493.         }
  494.         else
  495.         {
  496.             goto Failed;
  497.         }
  498.         
  499.         {
  500.             if ( !IsSubString(itemName, matchName) )
  501.             {
  502.                 goto Failed;
  503.             }
  504.             else if ( searchBits == fsSBPartialName )
  505.             {
  506.                 /* optimize for name matching only since it is most common way to search */
  507.                 goto Hit;
  508.             }
  509.         }
  510.     }
  511.     
  512.     if ( (searchBits & fsSBFullName) != 0 )
  513.     {
  514.         /* Use the same non-international call the File Manager uses */
  515.         if ( !EqualString(cPB->hFileInfo.ioNamePtr, matchName, false, true) )
  516.         {
  517.             goto Failed;
  518.         }
  519.         else if ( searchBits == fsSBFullName )
  520.         {
  521.             /* optimize for name matching only since it is most common way to search */
  522.             goto Hit;
  523.         }
  524.     }
  525.     
  526.     if ( (searchBits & fsSBFlParID) != 0 )
  527.     {
  528.         if ( ((unsigned long)(cPB->hFileInfo.ioFlParID) < (unsigned long)(searchInfo1->hFileInfo.ioFlParID)) ||
  529.              ((unsigned long)(cPB->hFileInfo.ioFlParID) > (unsigned long)(searchInfo2->hFileInfo.ioFlParID)) )
  530.         {
  531.             goto Failed;
  532.         }
  533.     }
  534.     
  535.     if ( (searchBits & fsSBFlAttrib) != 0 )
  536.     {
  537.         if ( ((cPB->hFileInfo.ioFlAttrib ^ searchInfo1->hFileInfo.ioFlAttrib) &
  538.               searchInfo2->hFileInfo.ioFlAttrib) != 0 )
  539.         {
  540.             goto Failed;
  541.         }
  542.     }
  543.     
  544.     if ( (searchBits & fsSBDrNmFls) != 0 )
  545.     {
  546.         if ( ((unsigned long)(cPB->dirInfo.ioDrNmFls) < (unsigned long)(searchInfo1->dirInfo.ioDrNmFls)) ||
  547.              ((unsigned long)(cPB->dirInfo.ioDrNmFls) > (unsigned long)(searchInfo2->dirInfo.ioDrNmFls)) )
  548.         {
  549.             goto Failed;
  550.         }
  551.     }
  552.  
  553.     if ( (searchBits & fsSBFlFndrInfo) != 0 )    /* fsSBFlFndrInfo is same as fsSBDrUsrWds */
  554.     {
  555.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlFndrInfo),
  556.                             (long *)&(searchInfo1->hFileInfo.ioFlFndrInfo),
  557.                             (long *)&(searchInfo2->hFileInfo.ioFlFndrInfo),
  558.                             sizeof(FInfo) / sizeof(long)) )
  559.         {
  560.             goto Failed;
  561.         }
  562.     }
  563.     
  564.     if ( (searchBits & fsSBFlXFndrInfo) != 0 )    /* fsSBFlXFndrInfo is same as fsSBDrFndrInfo */
  565.     {
  566.         if ( !CompareMasked((long *)&(cPB->hFileInfo.ioFlXFndrInfo),
  567.                             (long *)&(searchInfo1->hFileInfo.ioFlXFndrInfo),
  568.                             (long *)&(searchInfo2->hFileInfo.ioFlXFndrInfo),
  569.                             sizeof(FXInfo) / sizeof(long)) )
  570.         {
  571.             goto Failed;
  572.         }
  573.     }
  574.     
  575.     if ( (searchBits & fsSBFlLgLen) != 0 )
  576.     {
  577.         if ( ((unsigned long)(cPB->hFileInfo.ioFlLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlLgLen)) ||
  578.              ((unsigned long)(cPB->hFileInfo.ioFlLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlLgLen)) )
  579.         {
  580.             goto Failed;
  581.         }
  582.     }
  583.  
  584.     if ( (searchBits & fsSBFlPyLen) != 0 )
  585.     {
  586.         if ( ((unsigned long)(cPB->hFileInfo.ioFlPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlPyLen)) ||
  587.              ((unsigned long)(cPB->hFileInfo.ioFlPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlPyLen)) )
  588.         {
  589.             goto Failed;
  590.         }
  591.     }
  592.  
  593.     if ( (searchBits & fsSBFlRLgLen) != 0 )
  594.     {
  595.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRLgLen)) ||
  596.              ((unsigned long)(cPB->hFileInfo.ioFlRLgLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRLgLen)) )
  597.         {
  598.             goto Failed;
  599.         }
  600.     }
  601.  
  602.     if ( (searchBits & fsSBFlRPyLen) != 0 )
  603.     {
  604.         if ( ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) < (unsigned long)(searchInfo1->hFileInfo.ioFlRPyLen)) ||
  605.              ((unsigned long)(cPB->hFileInfo.ioFlRPyLen) > (unsigned long)(searchInfo2->hFileInfo.ioFlRPyLen)) )
  606.         {
  607.             goto Failed;
  608.         }
  609.     }
  610.  
  611.     if ( (searchBits & fsSBFlCrDat) != 0 )    /* fsSBFlCrDat is same as fsSBDrCrDat */
  612.     {
  613.         if ( ((unsigned long)(cPB->hFileInfo.ioFlCrDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlCrDat)) ||
  614.              ((unsigned long)(cPB->hFileInfo.ioFlCrDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlCrDat)) )
  615.         {
  616.             goto Failed;
  617.         }
  618.     }
  619.  
  620.     if ( (searchBits & fsSBFlMdDat) != 0 )    /* fsSBFlMdDat is same as fsSBDrMdDat */
  621.     {
  622.         if ( ((unsigned long)(cPB->hFileInfo.ioFlMdDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlMdDat)) ||
  623.              ((unsigned long)(cPB->hFileInfo.ioFlMdDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlMdDat)) )
  624.         {
  625.             goto Failed;
  626.         }
  627.     }
  628.  
  629.     if ( (searchBits & fsSBFlBkDat) != 0 )    /* fsSBFlBkDat is same as fsSBDrBkDat */
  630.     {
  631.         if ( ((unsigned long)(cPB->hFileInfo.ioFlBkDat) < (unsigned long)(searchInfo1->hFileInfo.ioFlBkDat)) ||
  632.              ((unsigned long)(cPB->hFileInfo.ioFlBkDat) > (unsigned long)(searchInfo2->hFileInfo.ioFlBkDat)) )
  633.         {
  634.             goto Failed;
  635.         }
  636.     }
  637.  
  638.     /* Hey, we passed all of the tests! */
  639.     
  640. Hit:
  641.     foundMatch = true;
  642.  
  643. /* foundMatch is false if code jumps to Failed */
  644. Failed:
  645.     /* Do we reverse our findings? */
  646.     if ( (searchBits & fsSBNegate) != 0 )
  647.     {
  648.         foundMatch = !foundMatch;    /* matches are not, not matches are */
  649.     }
  650.     
  651.     if ( foundMatch )
  652.     {
  653.  
  654.         /* Move the match into the match buffer */
  655.         userPB->ioMatchPtr[userPB->ioActMatchCount].vRefNum = cPB->hFileInfo.ioVRefNum;
  656.         userPB->ioMatchPtr[userPB->ioActMatchCount].parID = cPB->hFileInfo.ioFlParID;
  657.         if ( cPB->hFileInfo.ioNamePtr[0] > 63 )
  658.         {
  659.             cPB->hFileInfo.ioNamePtr[0] = 63;
  660.         }
  661.         BlockMoveData(cPB->hFileInfo.ioNamePtr,
  662.                       userPB->ioMatchPtr[userPB->ioActMatchCount].name,
  663.                       cPB->hFileInfo.ioNamePtr[0] + 1);
  664.         
  665.         /* increment the actual count */
  666.         ++(userPB->ioActMatchCount);
  667.     }
  668. }
  669.  
  670. /*****************************************************************************/
  671.  
  672. /*
  673. **    TimeOutTask is executed when the timer goes off. It simply sets the
  674. **    stopSearch field to true. After each object is found and possibly added
  675. **    to the matches buffer, stopSearch is checked to see if the search should
  676. **    continue.
  677. */
  678.  
  679. #if    __WANTPASCALELIMINATION
  680. #undef    pascal
  681. #endif
  682.  
  683. #if GENERATINGCFM
  684.  
  685. static    pascal    void    TimeOutTask(TMTaskPtr tmTaskPtr)
  686. {
  687.     ((ExtendedTMTaskPtr)tmTaskPtr)->stopSearch = true;
  688. }
  689.  
  690. #else
  691.  
  692. static    pascal    TMTaskPtr    GetTMTaskPtr(void)
  693.     ONEWORDINLINE(0x2e89);    /* MOVE.L A1,(SP) */
  694.     
  695. static    void    TimeOutTask(void)
  696. {
  697.     ((ExtendedTMTaskPtr)GetTMTaskPtr())->stopSearch = true;
  698. }
  699.  
  700. #endif
  701.  
  702. #if    __WANTPASCALELIMINATION
  703. #define    pascal    
  704. #endif
  705.  
  706. /*****************************************************************************/
  707.  
  708. /*
  709. **    GetDirModDate returns the modification date of a directory. If there is
  710. **    an error getting the modification date, -1 is returned to indicate
  711. **    something went wrong.
  712. */
  713. static    long    GetDirModDate(short vRefNum,
  714.                               long dirID)
  715. {
  716.     CInfoPBRec pb;
  717.     Str31 tempName;
  718.     long modDate;
  719.  
  720.     /* Protection against File Sharing problem */
  721.     tempName[0] = 0;
  722.     pb.dirInfo.ioNamePtr = tempName;
  723.     pb.dirInfo.ioVRefNum = vRefNum;
  724.     pb.dirInfo.ioDrDirID = dirID;
  725.     pb.dirInfo.ioFDirIndex = -1;    /* use ioDrDirID */
  726.     
  727.     if ( PBGetCatInfoSync(&pb) == noErr )
  728.     {
  729.         modDate = pb.dirInfo.ioDrMdDat;
  730.     }
  731.     else
  732.     {
  733.         modDate = -1;
  734.     }
  735.     
  736.     return ( modDate );
  737. }
  738.  
  739. /*****************************************************************************/
  740.  
  741. pascal    OSErr    IndexedSearch(CSParamPtr pb,
  742.                               long dirID)
  743. {
  744.     static LevelRecHandle    searchStack = NULL;        /* static handle to LevelRec stack */
  745.     static Size                searchStackSize = 0;    /* size of static handle */
  746.     SearchPositionRecPtr    catPosition;
  747.     long                    modDate;
  748.     short                    index;
  749.     ExtendedTMTask            timerTask;
  750.     OSErr                    result;
  751.     short                    realVRefNum;
  752.     Str63                    itemName;
  753.     CInfoPBRec                cPB;
  754.     long                    tempLong;
  755.     Boolean                    includeFiles;
  756.     Boolean                    includeDirs;
  757.     Boolean                    includeNames;
  758.     Str63                    upperName;
  759.     
  760.     timerTask.stopSearch = false;    /* don't stop yet! */
  761.     
  762.     /* If request has a timeout, install a Time Manager task. */
  763.     if ( pb->ioSearchTime != 0 )
  764.     {
  765.         /* Start timer */
  766.         timerTask.theTask.tmAddr = NewTimerProc(TimeOutTask);
  767.         InsTime((QElemPtr)&(timerTask.theTask));
  768.         PrimeTime((QElemPtr)&(timerTask.theTask), pb->ioSearchTime);
  769.     }
  770.     
  771.     /* Check the parameter block passed for things that we don't want to assume */
  772.     /* are OK later in the code. For example, make sure pointers to data structures */
  773.     /* and buffers are not NULL.  And while we're in there, see if the request */
  774.     /* specified searching for files, directories, or both, and see if the search */
  775.     /* was by full or partial name. */
  776.     result = VerifyUserPB(pb, &includeFiles, &includeDirs, &includeNames);
  777.     if ( result == noErr )
  778.     {
  779.         pb->ioActMatchCount = 0;    /* no matches yet */
  780.     
  781.         if ( includeNames )
  782.         {
  783.             /* The search includes seach by full or partial name. */
  784.             /* Make an upper case copy of the match string to pass to */
  785.             /* CheckForMatches. */
  786.             BlockMoveData(pb->ioSearchInfo1->hFileInfo.ioNamePtr,
  787.                             upperName,
  788.                             pb->ioSearchInfo1->hFileInfo.ioNamePtr[0] + 1);
  789.             /* Use the same non-international call the File Manager uses */
  790.             UpperString(upperName, true);
  791.         }
  792.         
  793.         /* Prevent casting to my type throughout code */
  794.         catPosition = (SearchPositionRecPtr)&pb->ioCatPosition;
  795.         
  796.         /* Create searchStack first time called */
  797.         if ( searchStack == NULL )
  798.         {
  799.             searchStack = (LevelRecHandle)NewHandle(kAdditionalLevelRecs * sizeof(LevelRec));
  800.         }
  801.         
  802.         /* Make sure searchStack really exists */
  803.         if ( searchStack != NULL )
  804.         {
  805.             searchStackSize = InlineGetHandleSize((Handle)searchStack);
  806.             
  807.             /* See if the search is a new search or a resumed search. */
  808.             if ( catPosition->initialize == 0 )
  809.             {
  810.                 /* New search. */
  811.                 
  812.                 /* Get the real vRefNum and fill in catPosition->initialize. */ 
  813.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &catPosition->initialize);
  814.                 if ( result == noErr )
  815.                 {
  816.                     /* clear searchStack */
  817.                     catPosition->stackDepth = 0;
  818.                     
  819.                     /* use dirID parameter passed and... */
  820.                     index = -1;    /* start with the passed directory itself! */
  821.                 }
  822.             }
  823.             else
  824.             {
  825.                 /* We're resuming a search. */
  826.     
  827.                 /* Get the real vRefNum and make sure catPosition->initialize is valid. */ 
  828.                 result = CheckVol(pb->ioNamePtr, pb->ioVRefNum, &realVRefNum, &tempLong);
  829.                 if ( result == noErr )
  830.                 {
  831.                     /* Make sure the resumed search is to the same volume! */
  832.                     if ( catPosition->initialize == tempLong )
  833.                     {
  834.                         /* For resume, catPosition->stackDepth > 0 */
  835.                         if ( catPosition->stackDepth > 0 )
  836.                         {
  837.                             /* Position catPosition->stackDepth to access last saved level */
  838.                             --(catPosition->stackDepth);
  839.             
  840.                             /* Get the dirID and index for the next item */
  841.                             dirID = (*searchStack)[catPosition->stackDepth].dirID;
  842.                             index = (*searchStack)[catPosition->stackDepth].index;
  843.                             
  844.                             /* Check the dir's mod date against the saved mode date on our "stack" */
  845.                             modDate = GetDirModDate(realVRefNum, dirID);
  846.                             if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  847.                             {
  848.                                 result = catChangedErr;
  849.                             }
  850.                         }
  851.                         else
  852.                         {
  853.                             /* Invalid catPosition record was passed */
  854.                             result = paramErr;
  855.                         }
  856.                     }
  857.                     else
  858.                     {
  859.                         /* The volume is not the same */
  860.                         result = catChangedErr;
  861.                     }
  862.                 }
  863.             }
  864.             
  865.             if ( result == noErr )
  866.             {
  867.                 /* ioNamePtr and ioVRefNum only need to be set up once. */
  868.                 cPB.hFileInfo.ioNamePtr = itemName;
  869.                 cPB.hFileInfo.ioVRefNum = realVRefNum;
  870.                 
  871.                 /*
  872.                 **    Here's the loop that:
  873.                 **        Finds the next item on the volume.
  874.                 **        If noErr, calls the code to check for matches and add matches
  875.                 **            to the match buffer.
  876.                 **        Sets up dirID and index for to find the next item on the volume.
  877.                 **
  878.                 **    The looping ends when:
  879.                 **        (a) an unexpected error is returned by PBGetCatInfo. All that
  880.                 **            is expected is noErr and fnfErr (after the last item in a
  881.                 **            directory is found).
  882.                 **        (b) the caller specified a timeout and our Time Manager task
  883.                 **            has fired.
  884.                 **        (c) the number of matches requested by the caller has been found.
  885.                 **        (d) the last item on the volume was found.
  886.                 */
  887.                 do
  888.                 {
  889.                     /* get the next item */
  890.                     cPB.hFileInfo.ioFDirIndex = index;
  891.                     cPB.hFileInfo.ioDirID = dirID;
  892.                     result = PBGetCatInfoSync(&cPB);
  893.                     if ( index != -1 )
  894.                     {
  895.                         if ( result == noErr )
  896.                         {
  897.                             /* We found something */
  898.         
  899.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  900.                             
  901.                             ++index;
  902.                             if ( (cPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  903.                             {
  904.                                 /* It's a directory */
  905.                                 
  906.                                 result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  907.                                 if ( result == noErr )
  908.                                 {
  909.                                     /* Save the current state on the searchStack */
  910.                                     /* when we come back, this is where we'll start */
  911.                                     (*searchStack)[catPosition->stackDepth].dirID = dirID;
  912.                                     (*searchStack)[catPosition->stackDepth].index = index;
  913.                                     (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  914.                                     
  915.                                     /* position catPosition->stackDepth for next saved level */
  916.                                     ++(catPosition->stackDepth);
  917.                                     
  918.                                     /* The next item to get is the 1st item in the child directory */
  919.                                     dirID = cPB.dirInfo.ioDrDirID;
  920.                                     index = 1;
  921.                                 }
  922.                             }
  923.                             /* else do nothing for files */
  924.                         }
  925.                         else
  926.                         {
  927.                             /* End of directory found (or we had some error and that */
  928.                             /* means we have to drop out of this directory). */
  929.                             /* Restore last thing put on stack and */
  930.                             /* see if we need to continue or quit. */
  931.                             if ( catPosition->stackDepth > 0 )
  932.                             {
  933.                                 /* position catPosition->stackDepth to access last saved level */
  934.                                 --(catPosition->stackDepth);
  935.                                 
  936.                                 dirID = (*searchStack)[catPosition->stackDepth].dirID;
  937.                                 index = (*searchStack)[catPosition->stackDepth].index;
  938.                                 
  939.                                 /* Check the dir's mod date against the saved mode date on our "stack" */
  940.                                 modDate = GetDirModDate(realVRefNum, dirID);
  941.                                 if ( modDate != (*searchStack)[catPosition->stackDepth].dirModDate )
  942.                                 {
  943.                                     result = catChangedErr;
  944.                                 }
  945.                                 else
  946.                                 {
  947.                                     /* Going back to ancestor directory. */
  948.                                     /* Clear error so we can continue. */
  949.                                     result = noErr;
  950.                                 }
  951.                             }
  952.                             else
  953.                             {
  954.                                 /* We hit the bottom of the stack, so we'll let the */
  955.                                 /* the eofErr drop us out of the loop. */
  956.                                 result = eofErr;
  957.                             }
  958.                         }
  959.                     }
  960.                     else
  961.                     {
  962.                         /* Special case for index == -1; that means that we're starting */
  963.                         /* a new search and so the first item to check is the directory */
  964.                         /* passed to us. */
  965.                         if ( result == noErr )
  966.                         {
  967.                             /* We found something */
  968.         
  969.                             CheckForMatches(&cPB, pb, upperName, includeFiles, includeDirs);
  970.                             
  971.                             /* Now, set the index to 1 and then we're ready to look inside */
  972.                             /* the passed directory. */
  973.                             index = 1;
  974.                         }
  975.                     }
  976.                 } while ( (!timerTask.stopSearch) &&    /* timer hasn't fired */
  977.                           (result == noErr) &&            /* no unexpected errors */
  978.                           (pb->ioReqMatchCount > pb->ioActMatchCount) ); /* we haven't found our limit */
  979.                 
  980.                 /* Did we drop out of the loop because of timeout or */
  981.                 /* ioReqMatchCount was found? */
  982.                 if ( result == noErr )
  983.                 {
  984.                     result = CheckStack(catPosition->stackDepth, searchStack, &searchStackSize);
  985.                     if ( result == noErr )
  986.                     {
  987.                         /* Either there was a timeout or ioReqMatchCount was reached. */
  988.                         /* Save the dirID and index for the next time we're called. */
  989.                         
  990.                         (*searchStack)[catPosition->stackDepth].dirID = dirID;
  991.                         (*searchStack)[catPosition->stackDepth].index = index;
  992.                         (*searchStack)[catPosition->stackDepth].dirModDate = GetDirModDate(realVRefNum, dirID);
  993.                         
  994.                         /* position catPosition->stackDepth for next saved level */
  995.                         
  996.                         ++(catPosition->stackDepth);
  997.                     }
  998.                 }
  999.             }
  1000.         }
  1001.         else
  1002.         {
  1003.             /* searchStack Handle could not be allocated */
  1004.             result = memFullErr;
  1005.         }
  1006.     }
  1007.     
  1008.     if ( pb->ioSearchTime != 0 )
  1009.     {
  1010.         /* Stop Time Manager task here if it was installed */
  1011.         RmvTime((QElemPtr)&(timerTask.theTask));
  1012.         DisposeRoutineDescriptor(timerTask.theTask.tmAddr);
  1013.     }
  1014.     
  1015.     return ( result );
  1016. }
  1017.  
  1018. /*****************************************************************************/
  1019.  
  1020. pascal OSErr PBCatSearchSyncCompat(CSParamPtr paramBlock)
  1021. {
  1022.     static Boolean            fullExtFSDispatchingtested = false;
  1023.     static Boolean            hasFullExtFSDispatching = false;
  1024.     OSErr                     result;
  1025.     Boolean                    supportsCatSearch;
  1026.     long                    response;
  1027.     GetVolParmsInfoBuffer    volParmsInfo;
  1028.     long                    infoSize;
  1029.     
  1030.     result = noErr;
  1031.  
  1032.     /* See if File Manager will pass CatSearch requests to external file systems */
  1033.     /* we'll store the results in a static variable so we don't have to call Gestalt */
  1034.     /* everytime we're called. */
  1035.     if ( !fullExtFSDispatchingtested )
  1036.     {
  1037.         fullExtFSDispatchingtested = true;
  1038.         if ( Gestalt(gestaltFSAttr, &response) == noErr )
  1039.         {
  1040.             hasFullExtFSDispatching = ((response & (1L << gestaltFullExtFSDispatching)) != 0);
  1041.         }
  1042.     }
  1043.     
  1044.     /* CatSearch is a per volume attribute, so we have to check each time we're */
  1045.     /* called to see if it is available on the volume specified. */
  1046.     supportsCatSearch = false;
  1047.     if ( hasFullExtFSDispatching )
  1048.     {
  1049.         infoSize = sizeof(GetVolParmsInfoBuffer);
  1050.         result = HGetVolParms(paramBlock->ioNamePtr, paramBlock->ioVRefNum,
  1051.                             &volParmsInfo, &infoSize);
  1052.         if ( result == noErr )
  1053.         {
  1054.             supportsCatSearch = hasCatSearch(volParmsInfo);
  1055.         }
  1056.     }
  1057.     
  1058.     /* noErr or paramErr is OK here. */
  1059.     /* paramErr just means that GetVolParms isn't supported by this volume */
  1060.     if ( (result == noErr) || (result == paramErr) )
  1061.     {
  1062.         if ( supportsCatSearch )
  1063.         {
  1064.             /* Volume supports CatSearch so use it. */
  1065.             /* CatSearch is faster than an indexed search. */
  1066.             result = PBCatSearchSync(paramBlock);
  1067.         }
  1068.         else
  1069.         {
  1070.             /* Volume doesn't support CatSearch so */
  1071.             /* search using IndexedSearch from root directory. */
  1072.             result = IndexedSearch(paramBlock, fsRtDirID);
  1073.         }
  1074.     }
  1075.     
  1076.     return ( result );
  1077. }
  1078.  
  1079. /*****************************************************************************/
  1080.  
  1081. pascal    OSErr    NameFileSearch(ConstStr255Param volName,
  1082.                                short vRefNum,
  1083.                                ConstStr255Param fileName,
  1084.                                FSSpecPtr matches,
  1085.                                long reqMatchCount,
  1086.                                long *actMatchCount,
  1087.                                Boolean newSearch,
  1088.                                Boolean partial)
  1089. {
  1090.     CInfoPBRec        searchInfo1, searchInfo2;
  1091.     HParamBlockRec    pb;
  1092.     OSErr            error;
  1093.     static CatPositionRec catPosition;
  1094.     static short    lastVRefNum = 0;
  1095.     
  1096.     /* get the real volume reference number */
  1097.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1098.     if ( error != noErr )
  1099.         return ( error );
  1100.     
  1101.     pb.csParam.ioNamePtr = NULL;
  1102.     pb.csParam.ioVRefNum = vRefNum;
  1103.     pb.csParam.ioMatchPtr = matches;
  1104.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1105.     if ( partial )    /* tell CatSearch what we're looking for: */
  1106.     {
  1107.         pb.csParam.ioSearchBits = fsSBPartialName + fsSBFlAttrib;    /* partial name file matches or */
  1108.     }
  1109.     else
  1110.     {
  1111.         pb.csParam.ioSearchBits =  fsSBFullName + fsSBFlAttrib;        /* full name file matches */
  1112.     }
  1113.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1114.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1115.     pb.csParam.ioSearchTime = 0;
  1116.     if ( (newSearch) ||                /* If caller specified new search */
  1117.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1118.     {
  1119.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1120.     }
  1121.     pb.csParam.ioCatPosition = catPosition;
  1122.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1123.  
  1124.     /* search for fileName */
  1125.     searchInfo1.hFileInfo.ioNamePtr = (StringPtr)fileName;
  1126.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1127.     
  1128.     /* only match files (not directories) */
  1129.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1130.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1131.  
  1132.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1133.     
  1134.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1135.          (error == eofErr) )                            /* found, then the call was successful so */
  1136.     {
  1137.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1138.     }
  1139.     else
  1140.     {
  1141.         *actMatchCount = 0;                            /* else no matches found */
  1142.     }
  1143.     
  1144.     if ( (error == noErr) ||                        /* If no errors */
  1145.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1146.     {
  1147.         catPosition = pb.csParam.ioCatPosition;
  1148.         lastVRefNum = vRefNum;
  1149.             /* we can probably start the next search where we stopped this time */
  1150.     }
  1151.     else
  1152.     {
  1153.         catPosition.initialize = 0;
  1154.             /* start the next search from beginning of catalog */
  1155.     }
  1156.     
  1157.     if ( pb.csParam.ioOptBuffer != NULL )
  1158.     {
  1159.         DisposePtr(pb.csParam.ioOptBuffer);
  1160.     }
  1161.         
  1162.     return ( error );
  1163. }
  1164.  
  1165. /*****************************************************************************/
  1166.  
  1167. pascal    OSErr    CreatorTypeFileSearch(ConstStr255Param volName,
  1168.                                       short vRefNum,
  1169.                                       OSType creator,
  1170.                                       OSType fileType,
  1171.                                       FSSpecPtr matches,
  1172.                                       long reqMatchCount,
  1173.                                       long *actMatchCount,
  1174.                                       Boolean newSearch)
  1175. {
  1176.     CInfoPBRec        searchInfo1, searchInfo2;
  1177.     HParamBlockRec    pb;
  1178.     OSErr            error;
  1179.     static CatPositionRec catPosition;
  1180.     static short    lastVRefNum = 0;
  1181.     
  1182.     /* get the real volume reference number */
  1183.     error = DetermineVRefNum(volName, vRefNum, &vRefNum);
  1184.     if ( error != noErr )
  1185.         return ( error );
  1186.     
  1187.     pb.csParam.ioNamePtr = NULL;
  1188.     pb.csParam.ioVRefNum = vRefNum;
  1189.     pb.csParam.ioMatchPtr = matches;
  1190.     pb.csParam.ioReqMatchCount = reqMatchCount;
  1191.     pb.csParam.ioSearchBits = fsSBFlAttrib + fsSBFlFndrInfo;    /* Looking for finder info file matches */
  1192.     pb.csParam.ioSearchInfo1 = &searchInfo1;
  1193.     pb.csParam.ioSearchInfo2 = &searchInfo2;
  1194.     pb.csParam.ioSearchTime = 0;
  1195.     if ( (newSearch) ||                /* If caller specified new search */
  1196.          (lastVRefNum != vRefNum) )    /* or if last search was to another volume, */
  1197.     {
  1198.         catPosition.initialize = 0;    /* then search from beginning of catalog */
  1199.     }
  1200.     pb.csParam.ioCatPosition = catPosition;
  1201.     pb.csParam.ioOptBuffer = GetTempBuffer(0x00004000, &pb.csParam.ioOptBufSize);
  1202.  
  1203.     /* no fileName */
  1204.     searchInfo1.hFileInfo.ioNamePtr = NULL;
  1205.     searchInfo2.hFileInfo.ioNamePtr = NULL;
  1206.     
  1207.     /* only match files (not directories) */
  1208.     searchInfo1.hFileInfo.ioFlAttrib = 0x00;
  1209.     searchInfo2.hFileInfo.ioFlAttrib = ioDirMask;
  1210.     
  1211.     /* search for creator; if creator = 0x00000000, ignore creator */
  1212.     searchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  1213.     if ( creator == (OSType)0x00000000 )
  1214.     {
  1215.         searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0x00000000;
  1216.     }
  1217.     else
  1218.     {
  1219.         searchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = (OSType)0xffffffff;
  1220.     }
  1221.     
  1222.     /* search for fileType; if fileType = 0x00000000, ignore fileType */
  1223.     searchInfo1.hFileInfo.ioFlFndrInfo.fdType = fileType;
  1224.     if ( fileType == (OSType)0x00000000 )
  1225.     {
  1226.         searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0x00000000;
  1227.     }
  1228.     else
  1229.     {
  1230.         searchInfo2.hFileInfo.ioFlFndrInfo.fdType = (OSType)0xffffffff;
  1231.     }
  1232.     
  1233.     /* zero all other FInfo fields */
  1234.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1235.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1236.     searchInfo1.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1237.     searchInfo1.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1238.     
  1239.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFlags = 0;
  1240.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
  1241.     searchInfo2.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
  1242.     searchInfo2.hFileInfo.ioFlFndrInfo.fdFldr = 0;
  1243.  
  1244.     error = PBCatSearchSyncCompat((CSParamPtr)&pb);
  1245.     
  1246.     if ( (error == noErr) ||                            /* If no errors or the end of catalog was */
  1247.          (error == eofErr) )                            /* found, then the call was successful so */
  1248.     {
  1249.         *actMatchCount = pb.csParam.ioActMatchCount;    /* return the match count */
  1250.     }
  1251.     else
  1252.     {
  1253.         *actMatchCount = 0;                            /* else no matches found */
  1254.     }
  1255.     
  1256.     if ( (error == noErr) ||                        /* If no errors */
  1257.          (error == catChangedErr) )                    /* or there was a change in the catalog */
  1258.     {
  1259.         catPosition = pb.csParam.ioCatPosition;
  1260.         lastVRefNum = vRefNum;
  1261.             /* we can probably start the next search where we stopped this time */
  1262.     }
  1263.     else
  1264.     {
  1265.         catPosition.initialize = 0;
  1266.             /* start the next search from beginning of catalog */
  1267.     }
  1268.     
  1269.     if ( pb.csParam.ioOptBuffer != NULL )
  1270.     {
  1271.         DisposePtr(pb.csParam.ioOptBuffer);
  1272.     }
  1273.         
  1274.     return ( error );
  1275. }
  1276.  
  1277. /*****************************************************************************/
  1278.